package me.zhengjie.minbearwcs.serivce.inbound;

import com.mysql.cj.log.Log;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.inbound.inf.InboundOrderDetailHistoryDao;
import me.zhengjie.minbearwcs.dao.inbound.inf.InboundOrderHistoryDao;
import me.zhengjie.minbearwcs.dao.mission.inf.WcsMissionDao;
import me.zhengjie.minbearwcs.dao.stock.inf.PalletDao;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.StorageDao;
import me.zhengjie.minbearwcs.entity.DTO.inbound.mes.InboundPalletConfirm;
import me.zhengjie.minbearwcs.entity.DTO.inbound.mes.InboundPalletContinue;
import me.zhengjie.minbearwcs.entity.DTO.inbound.mes.InboundPalletRollback;
import me.zhengjie.minbearwcs.entity.DTO.inbound.mes.order.MesInboundMediumBox;
import me.zhengjie.minbearwcs.entity.DTO.inbound.mes.order.MesInboundOrder;
import me.zhengjie.minbearwcs.entity.DTO.inbound.mes.order.MesInboundPallet;
import me.zhengjie.minbearwcs.entity.DTO.inbound.wcs.InboundMissionFactor;
import me.zhengjie.minbearwcs.entity.DTO.outbound.pda.OutboundPdaContentPool;
import me.zhengjie.minbearwcs.entity.DTO.pda.PdaCallAgvDTO;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.history.InboundOrderDetailHistory;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.history.InboundOrderHistory;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.WcsMission;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.InboundMissionFor5Or6FloorStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.Pallet;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.SmallStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.type.StorageType;
import me.zhengjie.minbearwcs.utils.JsonUtil;
import me.zhengjie.minbearwcs.utils.WcsHpptUtil;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import me.zhengjie.minbearwcs.dao.inbound.inf.InboundOrderDao;
import me.zhengjie.minbearwcs.dao.inbound.inf.InboundOrderDetailDao;
import me.zhengjie.minbearwcs.dao.stock.inf.MaterialStockDao;
import me.zhengjie.minbearwcs.entity.DTO.inbound.mes.InboundMesCallAgv;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.InboundOrder;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.InboundOrderDetail;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.status.InboundOrderDetailStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.inbound.status.InboundOrderStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.MaterialStock;
import me.zhengjie.minbearwcs.serivce.mission.generate.MissionGeneratorCollector;
import me.zhengjie.minbearwcs.serivce.mission.generate.type.MissionGenType;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

import javax.crypto.interfaces.PBEKey;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class InboundService {

    private final InboundOrderDao inboundOrderDao;
    private final InboundOrderDetailDao inboundOrderDetailDao;
    private final MaterialStockDao materialStockDao;
    private final PalletDao palletDao;
    private final MissionGeneratorCollector missionGeneratorCollector;
    private final SmallStorageDao smallStorageDao;
    private final TransactionTemplate transactionTemplate;
    private final WcsMissionDao wcsMissionDao;
    private final InboundOrderDetailHistoryDao inboundOrderDetailHistoryDao;
    private final InboundOrderHistoryDao inboundOrderHistoryDao;
    private final WcsHpptUtil wcsHpptUtil;
    private final StorageDao storageDao;


    public boolean receiveInboundOrder(MesInboundOrder receive) {
        Boolean execute = transactionTemplate.execute(status -> {
            try {
                InboundOrder selectByInboundOrderCode = inboundOrderDao.selectByInboundOrderCode(receive.getInboundOrderCode());
                // 判断单有没有
                if (selectByInboundOrderCode == null) {
                    InboundOrder inboundOrder = new InboundOrder(null, receive.getInboundOrderCode(), receive.getOrderType(),
                            InboundOrderStatus.READY, LocalDateTime.now(), null, null);
                    inboundOrderDao.insert(inboundOrder);
                }

                for (MesInboundPallet palletReceive : receive.getPalletList()) {
                    // 判断detail录入没有
                    Integer selectIdByInboundOrderAndPallet = inboundOrderDetailDao.selectIdByInboundOrderAndPallet(receive.getInboundOrderCode(), palletReceive.getPalletCode());
                    if (selectIdByInboundOrderAndPallet != null) continue;

                    InboundOrderDetail inboundOrderDetail = new InboundOrderDetail(null, receive.getInboundOrderCode(), palletReceive.getPalletCode(),
                            palletReceive.getMaterialCode(), palletReceive.getProductionLine(),
                            InboundOrderDetailStatus.WAIT_FOR_CALL_AGV, null, null, null);
                    inboundOrderDetailDao.insert(inboundOrderDetail);

                    long materialNum = palletReceive.getMediumList()
                            .stream()
                            .map(MesInboundMediumBox::getSmallBoxCodeList)
                            .mapToLong(Collection::size)
                            .sum();

                    Pallet pallet = new Pallet(null, palletReceive.getPalletCode(), palletReceive.getMaterialCode(),
                            palletReceive.getProductionLine(), receive.getInboundOrderCode(), null,
                            null, (int) materialNum, 0, 0, null,
                            OutboundPdaContentPool.IS_NOT_FAKER_PALLET);
                    palletDao.insert(pallet);

                    for (MesInboundMediumBox mesInboundMediumBox : palletReceive.getMediumList()) {
                        for (String smallBox : mesInboundMediumBox.getSmallBoxCodeList()) {
                            MaterialStock materialStock = new MaterialStock(null, smallBox, mesInboundMediumBox.getMediumBoxCode(),
                                    palletReceive.getMaterialCode(), palletReceive.getPalletCode(),
                                    receive.getInboundOrderCode(), null, null,
                                    palletReceive.getProductionLine());
                            materialStockDao.insert(materialStock);
                        }
                    }

                }

                return true;
            } catch (Exception e) {
                log.error("栈板{}，入库单：{} ，插入失败", receive.getInboundOrderCode(), receive.getInboundOrderCode(), e);
                status.setRollbackOnly();
                return false;
            }
        });

        return execute;
    }

    @Transactional
    public Tuple<String> inboundOrderComplete(String inboundOrderCode) {
        List<InboundOrderDetail> inboundOrderDetailList = inboundOrderDetailDao.selectByInboundOrderCode(inboundOrderCode);

        long count = inboundOrderDetailList.stream()
                .filter(a -> InboundOrderDetailStatus.COMPLETE != a.getStatus() && InboundOrderDetailStatus.COMPLETE_ROLLBACK != a.getStatus())
                .count();

        if (count > 0) {
            return Tuple.tuple("该订单未完成，不能结束", false);
        }

        for (InboundOrderDetail inboundOrderDetail : inboundOrderDetailList) {
            inboundOrderDetailHistoryDao.insert(
                    new InboundOrderDetailHistory(null, inboundOrderDetail.getInboundOrderCode(),
                            inboundOrderDetail.getPalletCode(), inboundOrderDetail.getMaterialCode(), inboundOrderDetail.getProductionLine(),
                            inboundOrderDetail.getStatus(), inboundOrderDetail.getStartPoint(), inboundOrderDetail.getExchangePoint(),
                            inboundOrderDetail.getStoragePoint())
            );
            inboundOrderDetailDao.deleteById(inboundOrderDetail.getId());

            if (inboundOrderDetail.getExchangePoint() == null) {
                String small = smallStorageDao.selectSmallStorageByQrPoint(inboundOrderDetail.getStoragePoint());
                StorageType storageType = storageDao.selectStorageTypeBySmallStorage(small);
                if (StorageType.STORAGE_AREA_3F == storageType || StorageType.STORAGE_AREA_4F == storageType) {
                    materialStockDao.deleteByPallet(inboundOrderDetail.getPalletCode());
                }
            }

//            materialStockDao.deleteByPallet(inboundOrderDetail.getPalletCode());
        }

        InboundOrder inboundOrder = inboundOrderDao.selectByInboundOrderCode(inboundOrderCode);
        inboundOrderHistoryDao.insert(
                new InboundOrderHistory(null, inboundOrder.getInboundOrderCode(),
                        inboundOrder.getOrderType(), InboundOrderStatus.COMPLETE, inboundOrder.getDispatchTime(),
                        inboundOrder.getStartTime(), LocalDateTime.now())
        );

        inboundOrderDao.deleteById(inboundOrder.getId());
        return Tuple.tuple(null, true);
    }


    public Tuple<String> receiveCallAgv(PdaCallAgvDTO pdaCallAgvDTO) {
        SmallStorage selectByQrCode = smallStorageDao.selectByQrCode(pdaCallAgvDTO.getStartPoint());
        if (selectByQrCode == null) {
            return Tuple.tuple("该地标不存在", false);
        }

        SmallStorage smallStorage = smallStorageDao.selectPalletIsInStock(pdaCallAgvDTO.getPalletCode());
        if (smallStorage != null) {
            return Tuple.tuple("请勿重复扫码", false);
        }


        MesInboundOrder mesInboundOrder = wcsHpptUtil.fetchInboundOrder(pdaCallAgvDTO.getPalletCode());
        log.info("获取到栈板 {} ，入库工单 ： {}", pdaCallAgvDTO.getPalletCode(), JsonUtil.getJsonInfo(mesInboundOrder));


        if (mesInboundOrder == null) {
            log.info("栈板 {} ， 对应入库工单Mes没找到", pdaCallAgvDTO.getPalletCode());
            return Tuple.tuple("没找到Mes对应栈板的入库单", false);
        }

        String inboundOrderCode = mesInboundOrder.getInboundOrderCode();

        Set<String> mesInboundPalletSet = mesInboundOrder.getPalletList()
                .stream()
                .map(MesInboundPallet::getPalletCode)
                .collect(Collectors.toSet());
        if (!mesInboundPalletSet.contains(pdaCallAgvDTO.getPalletCode())) {
            return Tuple.tuple("入库栈板不存在工单里", false);
        }

        // 插入工单
        boolean resInsert = receiveInboundOrder(mesInboundOrder);
        if (!resInsert) {
            return Tuple.tuple("入库单生成失败", false);
        }

        // old

        if (!smallStorageDao.judgeStorageCanUseByQrPoint(pdaCallAgvDTO.getStartPoint())) {
            return Tuple.tuple("该地标存在栈板，请勿重复绑定", false);
        }

        InboundOrderDetail inboundOrderDetail = inboundOrderDetailDao.selectDetailByOrderCodeAndPalletCode(
                inboundOrderCode,
                pdaCallAgvDTO.getPalletCode(),
                InboundOrderDetailStatus.WAIT_FOR_CALL_AGV
        );

        if (inboundOrderDetail == null) {
            return Tuple.tuple("任务已开始或者不存在", false);
        }

        Boolean res = transactionTemplate.execute(status -> {
            try {
                inboundOrderDetailDao.updateStartPointById(inboundOrderDetail.getId(), pdaCallAgvDTO.getStartPoint());
                return true;
            } catch (Exception e) {
                log.error("栈板{}，绑定失败", pdaCallAgvDTO.getPalletCode(), e);
                status.setRollbackOnly();
            }
            return false;
        });

        if (Boolean.FALSE.equals(res)) {
            return Tuple.tuple("叫车失败", false);
        }

        InboundMissionFactor factor = new InboundMissionFactor(inboundOrderDetail.getInboundOrderCode(),
                pdaCallAgvDTO.getPalletCode(),
                pdaCallAgvDTO.getStartPoint(),
                inboundOrderDetail.getId());
        Tuple generate = missionGeneratorCollector.getGenerator(MissionGenType.INBOUND).generate(factor);

        if (generate.success) {
            transactionTemplate.executeWithoutResult(status -> {
                try {
                    smallStorageDao.updatePalletCodeByQrPoint(pdaCallAgvDTO.getStartPoint(), pdaCallAgvDTO.getPalletCode());
                } catch (Exception e) {
                    log.error("栈板{}，绑定点位 {} 失败", pdaCallAgvDTO.getPalletCode(), pdaCallAgvDTO.getStartPoint(), e);
                    status.setRollbackOnly();
                }
            });
        }

        return generate;
    }


    @Transactional
    public Tuple<String> inboundPalletConfirm(InboundPalletConfirm inboundPalletConfirm) {
        log.info("收到wms入库确认是否账物一致信息 ： {}", JsonUtil.getJsonInfo(inboundPalletConfirm));
        Integer detailId = inboundOrderDetailDao.selectIdByInboundOrderAndPallet(
                inboundPalletConfirm.getInboundOrderCode(),
                inboundPalletConfirm.getPalletCode()
        );

        if (detailId == null) {
            log.info("没找到对应工单 {} 及其栈板 {} ", inboundPalletConfirm.getInboundOrderCode(), inboundPalletConfirm.getPalletCode());
            return Tuple.tuple("没找到对应工单及其栈板", false);
        }

        WcsMission wcsMission = wcsMissionDao.selectByOrderCodeAndDetailId(
                inboundPalletConfirm.getInboundOrderCode(),
                detailId
        );

        if (InboundMissionFor5Or6FloorStatus.WAIT_FOR_CHECK_SKU == wcsMission.getEnumMissionStatus()) {
            if (inboundPalletConfirm.getIsConfirm() == 1) {
                log.info("入库单号： {} ，栈板： {} ，确认账物一致生成入库任务",
                        inboundPalletConfirm.getInboundOrderCode(),
                        inboundPalletConfirm.getPalletCode()
                );
                wcsMissionDao.updateMissionStatusByOrderCodeAndDetailId(
                        inboundPalletConfirm.getInboundOrderCode(),
                        detailId,
                        InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_REAL_STORAGE.name()
                );
                return Tuple.tuple("确认账物一致生成入库任务", true);
            } else {
                log.info("入库单号： {} ，栈板： {} ，确认账物一致一致有问题，进入异常收料流程",
                        inboundPalletConfirm.getInboundOrderCode(),
                        inboundPalletConfirm.getPalletCode()
                );
                wcsMissionDao.updateMissionStatusByOrderCodeAndDetailId(
                        inboundPalletConfirm.getInboundOrderCode(),
                        detailId,
                        InboundMissionFor5Or6FloorStatus.WAIT_FOR_ROLLBACK_PRODUCTION_LINE.name()
                );
                return Tuple.tuple("确认账物一致一致有问题，进入异常收料流程", true);
            }
        } else {
            log.info("wcs任务 {} 已确认，当前任务状态为 {}", wcsMission.getMissionId(), wcsMission.getEnumMissionStatus());
            return Tuple.tuple("已确认或任务不存在，请勿重复操作", false);
        }


    }


    public Tuple<String> inboundPalletContinue(InboundPalletContinue inboundPalletContinue) {
        log.info("收到wms入库异常处理完成后，继续入库信息 ： {}", inboundPalletContinue);

        Integer detailId = inboundOrderDetailDao.selectIdByInboundOrderAndPallet(
                inboundPalletContinue.getInboundOrderCode(),
                inboundPalletContinue.getPalletCode()
        );

        wcsMissionDao.updateMissionStatusByOrderCodeAndDetailId(
                inboundPalletContinue.getInboundOrderCode(),
                detailId,
                InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_REAL_STORAGE.name()
        );
        return Tuple.tuple("", true);
    }


    public Tuple<String> inboundPalletRollback(InboundPalletRollback inboundPalletRollback) {
        log.info("收到wms入库异常后，退回产线通知信息 ： {}", inboundPalletRollback);

        Integer detailId = inboundOrderDetailDao.selectIdByInboundOrderAndPallet(
                inboundPalletRollback.getInboundOrderCode(),
                inboundPalletRollback.getPalletCode()
        );

        wcsMissionDao.updateMissionStatusByOrderCodeAndDetailId(
                inboundPalletRollback.getInboundOrderCode(),
                detailId,
                InboundMissionFor5Or6FloorStatus.WAIT_FOR_ROLLBACK_PRODUCTION_LINE.name()
        );
        return Tuple.tuple("", true);
    }

}
